﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TextPioneer
{
	public class Corpus
	{
		public int Id { get; private set; }
		public string Name { get; private set; }
		public bool IsBase { get; private set; }

		public Corpus(int id, string name, bool isBase)
		{
			Id = id;
			Name = name;
			IsBase = isBase;
		}
	}

	public class TopicNode
	{
		public int Id { get; private set; }
		public string Name { get; private set; }
		public int Depth { get; private set; }
		public double LeadingFactor { get; private set; }
		public Corpus Corpus { get; private set; }
		public DateTime StartTime { get; private set; }
		public DateTime EndTime { get; private set; }
		public List<Keyword> KeywordsTF { get; set; }
		public List<Keyword> KeywordsTFIDF { get; set; }

		public TopicNode(int id, string name, int depth, double leadingFactor, Corpus corpus, DateTime start, DateTime end)
		{
			Id = id;
			Name = name;
			Depth = depth;
			LeadingFactor = leadingFactor;
			Corpus = corpus;
			StartTime = start;
			EndTime = end;

			KeywordsTF = new List<Keyword>();
			KeywordsTFIDF = new List<Keyword>();
		}
	}

	public abstract class TopicBundle : TopicNode
	{
		public abstract TopicNode[] GetChildren();

		public TopicBundle(int id, string name, int depth, double leadingFactor, Corpus corpus, DateTime start, DateTime end)
			:base(id, name, depth, leadingFactor, corpus, start, end)
		{
		}

		public override bool Equals(object obj)
		{
			if (obj is TopicBundle)
				return this.Id == (obj as TopicBundle).Id;
			else
				return false;
		}
		public override int GetHashCode()
		{
			return Id;
		}
	}

	public abstract class Topic : TopicNode
	{
		public abstract int[] GetDocumentIds(DateTime startDate, DateTime endTime);

		public Topic(int id, string name, int depth, double leadingFactor, Corpus corpus, DateTime start, DateTime end)
			:base(id, name, depth, leadingFactor, corpus, start, end)
		{
		}

		public override bool Equals(object obj)
		{
			if (obj is Topic)
				return this.Id == (obj as Topic).Id;
			else
				return false;
		}
		public override int GetHashCode()
		{
			return Id;
		}
	}

	public struct DocumentSimilarityPair
	{
		public int DocumentID1 { get; set; }
		public int DocumentID2 { get; set; }
		public double Similarity { get; set; }

		public DocumentSimilarityPair(int docID1, int docID2, double similarity)
			:this()
		{
			DocumentID1 = docID1;
			DocumentID2 = docID2;
			Similarity = similarity;
		}
	}

	public struct ActivePoint
	{
		public Topic Topic { get; set; }
		public DateTime Time { get; set; }
		public bool IsMovable { get; set; }

		public ActivePoint(Topic topic, DateTime time, bool isMovable)
			: this()
		{
			Topic = topic;
			Time = time;
			IsMovable = isMovable;
		}
	}

    public abstract class Entity
	{
		public abstract string Text { get; set; }
		//public abstract Corpus[] RelatedCorpora { get; set; }
		//public abstract Topic[] RelatedTopics { get; set; }
		//public abstract ActivePoint[] ActivePoints { get; set; }
		public abstract object ViewData { get; set; }

		public Entity(/*string content, int iconIndex*/)
		{
			//Text = content;
		}
	}

	public struct Keyword
	{
		public string Content { get; set; }
		public double Rank { get; set; }

		public Keyword(string word, double rank)
			: this()
		{
			Content = word;
			Rank = rank;
		}
	}

	public struct LeadingTrendPoint
	{
		public DateTime Time { get; set; }
		public double Value1 { get; set; }
		public double Value2 { get; set; }

		public LeadingTrendPoint(DateTime time, double v1, double v2)
			:this()
		{
			Time = time;
			Value1 = v1;
			Value2 = v2;
		}
	}

	public enum KeywordRanking
	{
 		TFIDF, TF
	}

	public interface IMoveEntityActivePointTask
	{
		void Wait();
		void Cancel();
		double GetProgress();
	}

	public struct EntityType
	{
		public string Name { get; set; }
		public Type EntityViewType { get; set; }

		public EntityType(string name, Type viewType)
			: this()
		{
			Name = name;
			EntityViewType = viewType;
		}
	}

	public interface IDataProvider : IDisposable
	{
		DateTime StartTime { get; }
		DateTime EndTime { get; }
		Corpus[] GetCorpora();
		TopicBundle[] GetTopLevelTopics(Corpus corpus);
		IEnumerable<LeadingTrendPoint> GetLeadingTrends(Topic topic1, Topic topic2);
		IEnumerable<DocumentSimilarityPair> GetSimilarityPairs(Topic topic1, Topic topic2);
		IEnumerable<Keyword> GetKeywords(TopicNode node, KeywordRanking ranking, int count);
		IEnumerable<EntityType> GetEntityTypes();
		IEnumerable<Entity> GetEntities(string typeName, Topic topic, DateTime date);
		IMoveEntityActivePointTask BeginMoveEntityActivePoint(Entity entity, ActivePoint originalPoint, Topic targetTopic);
		void Initialize(string dbPath);
	}
}
